/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package za.co.pas.business.data;

import java.io.Serializable;
import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.CascadeType;
//import za.co.pas.spaceandtime.data.Building;
import javax.persistence.Transient;
import za.co.pas.business.constant.Constant;
import za.co.pas.business.data.audit.Company_AUDIT;

/**
 *
 * @author alabuschagne
 */
@Entity
public class Company extends GenericData implements Serializable, Comparable
{

    private static final long serialVersionUID = 1L;
    private List<UserLoginDetails> users = new LinkedList<UserLoginDetails>();
    private String tradingAs = "";
    private Address streetAddress;
    private Address postalAddress;
    private Boolean subscriber = Boolean.FALSE;
    private Boolean enabled = Boolean.TRUE;
    private String code = "";
    //Transient
    private List<Company_AUDIT> companyAuditList = new LinkedList<Company_AUDIT>();
    private boolean readyToChange = false;

    public Company()
    {
        streetAddress = new Address();
        postalAddress = new Address();
    }

    @Override
    public int hashCode()
    {
        return super.hashCode();
    }

    @Override
    public boolean equals(Object object)
    {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Company))
        {
            return false;
        }
        Company other = (Company) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id)))
        {
            return false;
        }
        return true;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder(getCode());
        sb.append(" | ");
        sb.append(super.toString());
        return sb.toString();
    }

    /**
     * @return the users
     */
    @ManyToMany(cascade = CascadeType.ALL)//, mappedBy = "company"
    public List<UserLoginDetails> getUsers()
    {
        return users;
    }

    /**
     * @param users the users to set
     */
    public void setUsers(List<UserLoginDetails> users)
    {
        this.users = users;
    }

    /**
     * @return the tradingAs
     */
    public String getTradingAs()
    {
        return tradingAs;
    }

    /**
     * @param tradingAs the tradingAs to set
     */
    public void setTradingAs(String tradingAs)
    {
        this.tradingAs = tradingAs;
    }

    /**
     * Many to one relationship is because the company address can be the same as the build as well as the person's address
     * @return the streetAddress
     */
    @ManyToOne(cascade = CascadeType.ALL)
    public Address getStreetAddress()
    {
        return streetAddress;
    }

    /**
     * @param streetAddress the streetAddress to set
     */
    public void setStreetAddress(Address streetAddress)
    {
        this.streetAddress = streetAddress;
    }

    /**
     * @return the postalAddress
     */
    @ManyToOne(cascade = CascadeType.ALL)
    public Address getPostalAddress()
    {
        return postalAddress;
    }

    /**
     * Many to one relationship is because the company address can be the same as the build as well as the person's address
     * @param postalAddress the postalAddress to set
     */
    public void setPostalAddress(Address postalAddress)
    {
        this.postalAddress = postalAddress;
    }

    /**
     * @return the subscriber
     */
    public Boolean getSubscriber()
    {
        return subscriber;
    }

    /**
     * @param subscriber the subscriber to set
     */
    public void setSubscriber(Boolean subscriber)
    {
        this.subscriber = subscriber;
    }

    /**
     * @return the enabled
     */
    public Boolean getEnabled()
    {
        return enabled;
    }

    /**
     * @param enabled the enabled to set
     */
    public void setEnabled(Boolean enabled)
    {
        this.enabled = enabled;
    }

    /**
     * Compare the two companies. Can only compare if this object was persisted
     * @param newCompany
     * @return True if it is identical, false if not identical
     */
    public boolean check(Company newCompany)
    {
        if (newCompany == null)
        {
            return false;
        }
        if (id == null)
        {
            return false;
        }
        else if (!super.check(newCompany))
        {
            return false;
        }
        else if ((tradingAs == null) && (newCompany.getTradingAs() != null))
        {
            return false;
        }
        else if ((tradingAs != null) && (tradingAs.compareTo(newCompany.getTradingAs()) != 0))
        {
            return false;
        }
        else if (subscriber.compareTo(newCompany.getSubscriber()) != 0)
        {
            return false;
        }
        else if (enabled.compareTo(newCompany.getEnabled()) != 0)
        {
            return false;
        }
        else if ((streetAddress == null) && (newCompany.getStreetAddress() != null))
        {
            return false;
        }
        else if ((streetAddress != null) && (!streetAddress.check(newCompany.getStreetAddress())))
        {
            return false;
        }
        else if ((postalAddress == null) && (newCompany.getPostalAddress() != null))
        {
            return false;
        }
        else if ((postalAddress != null) && (!postalAddress.check(newCompany.getPostalAddress())))
        {
            return false;
        }
        if ((users == null) && (newCompany.getUsers() != null))
        {
            return false;
        }
        else if (users != null)
        {
            if (newCompany.getUsers() == null)
            {
                return false;
            }
            if (users.size() != newCompany.getUsers().size())
            {
                return false;
            }

            ListIterator<UserLoginDetails> it = users.listIterator();
            while (it.hasNext())
            {
                boolean found = false;
                UserLoginDetails uld = it.next();
                ListIterator<UserLoginDetails> itNew = newCompany.getUsers().listIterator();
                while (itNew.hasNext())
                {
                    if (uld.check(itNew.next()))
                    {
                        found = true;
                        break;//out of inner loop
                    }
                }
                if (!found)
                {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public Company clone()
    {
        Company newCompany = new Company();
        newCompany.setCode(code);
        newCompany.setDescription(description);
        newCompany.setId(id);
        newCompany.setName(name);
        newCompany.setPostalAddress(postalAddress.clone());
        newCompany.setStreetAddress(streetAddress.clone());
        newCompany.setSubscriber(subscriber);
        newCompany.setTradingAs(tradingAs);
        if (users != null)
        {
            ListIterator<UserLoginDetails> it = users.listIterator();
            List<UserLoginDetails> b = new LinkedList<UserLoginDetails>();
            while (it.hasNext())
            {
                b.add(it.next().clone());
            }
            newCompany.setUsers(b);
        }
        return newCompany;
    }

    public void add(UserLoginDetails uld)
    {
        if (users == null)
        {
            users = new LinkedList<UserLoginDetails>();
            setUsers(users);
        }
        users.add(uld);
    }

    @Override
    public int compareTo(Object o)
    {
        if (o instanceof Company)
        {
            Company c = (Company) o;
            return getName().compareTo(c.getName());
        }
        return -1;
    }

    /**
     * @return the code
     */
    public String getCode()
    {
        return code;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code)
    {
        this.code = code;
    }

    private void makeAudit(String colName, UserLoginDetails uld, Object oldValue, Object newValue)
    {
        Company_AUDIT ca = new Company_AUDIT();
        ca.setColName(colName);
        ca.setChangedAt(Calendar.getInstance());
        ca.setChangedBy(uld);
        if (getId() != null)
        {
            ca.setCompany(this);
        }
        else
        {
            ca.setCompany(null);//new
        }
        ca.setLongId(getId());
        if (newValue != null)
        {
            ca.setNewValue(newValue.toString());
        }
        else
        {
            ca.setNewValue(null);
        }

        if (oldValue != null)
        {
            ca.setOldValue(oldValue.toString());
        }
        else
        {
            ca.setOldValue(null);
        }
        add(ca);
    }

    public boolean set(Company newCompany, UserLoginDetails uld)
    {
        boolean updated = false;
        if (newCompany == null)
        {
            return updated;
        }

        if (!Constant.stringCompare(getCode(), newCompany.getCode()))
        {
            makeAudit("CODE", uld, getCode(), newCompany.getCode());
            setCode(newCompany.getCode());
            updated = true;
        }
        if (!Constant.stringCompare(getDescription(), newCompany.getDescription()))
        {
            makeAudit("DESCRIPTION", uld, getDescription(), newCompany.getDescription());
            setDescription(newCompany.getDescription());
            updated = true;
        }
        if (!Constant.booleanCompare(getEnabled(), newCompany.getEnabled()))
        {
            makeAudit("ENABLED", uld, getEnabled(), newCompany.getEnabled());
            setEnabled(newCompany.getEnabled());
            updated = true;
        }
        if (!Constant.stringCompare(getName(), newCompany.getName()))
        {
            makeAudit("NAME", uld, getName(), newCompany.getName());
            setName(newCompany.getName());
            updated = true;
        }
        if (!Constant.booleanCompare(getSubscriber(), newCompany.getSubscriber()))
        {
            makeAudit("SUBSCRIBER", uld, getSubscriber(), newCompany.getSubscriber());
            setSubscriber(newCompany.getSubscriber());
            updated = true;
        }
        if (!Constant.stringCompare(getTradingAs(), newCompany.getTradingAs()))
        {
            makeAudit("TRADINGAS", uld, getTradingAs(), newCompany.getTradingAs());
            setTradingAs(newCompany.getTradingAs());
            updated = true;
        }
        Address sa = getStreetAddress();
        boolean update = false;
        if ((sa == null) && (newCompany.getStreetAddress() != null))
        {
            sa = new Address();
            update = true;
        }
        else if ((sa != null) && (newCompany.getStreetAddress() == null))
        {
            update = true;
        }
        else if ((sa != null) && (newCompany.getStreetAddress() != null))
        {
            update = !sa.check(newCompany.getStreetAddress());
        }
        if (update)
        {
            updated |= sa.set(this, uld, newCompany.getStreetAddress());
        }
        Address pa = getPostalAddress();
        update = false;
        if ((pa == null) && (newCompany.getPostalAddress() != null))
        {
            pa = new Address();
            update = true;
        }
        else if ((pa != null) && (newCompany.getPostalAddress() == null))
        {
            update = true;
        }
        else if ((pa != null) && (newCompany.getPostalAddress() != null))
        {
            update = !pa.check(newCompany.getPostalAddress());
        }
        if (update)
        {
            updated |= pa.set(this, uld, newCompany.getPostalAddress());
        }

        //Set all processed false
        if (newCompany.getUsers() != null)
        {
            ListIterator<UserLoginDetails> itN = newCompany.getUsers().listIterator();
            while (itN.hasNext())
            {
                UserLoginDetails uN = itN.next();
                uN.setProcessed(false);
            }
        }
        List<UserLoginDetails> ulds = getUsers();
        if (ulds == null)
        {
            ulds = new LinkedList<UserLoginDetails>();
        }
        ListIterator<UserLoginDetails> it = ulds.listIterator();

        while (it.hasNext())
        {
            UserLoginDetails u = it.next();
            u.setProcessed(false);
            if (newCompany.getUsers() != null)
            {
                ListIterator<UserLoginDetails> itN = newCompany.getUsers().listIterator();
                while (itN.hasNext())
                {
                    UserLoginDetails uN = itN.next();
                    if (u.getId() == uN.getId())
                    {
                        updated |= u.set(this, uld, uN, false);
                        u.setProcessed(true);
                        uN.setProcessed(true);
                        break;//out of inner loop
                    }
                }
            }
        }
        //if it is in the old list but not in the new list, then remove it from this list
        it = ulds.listIterator();
        while (it.hasNext())
        {
            UserLoginDetails u = it.next();
            if (!u.isProcessed())
            {
                //Remove user from this company
                ulds.remove(u);
                //Remove company from user
                u.removeCompany(this);
                updated = true;
            }
        }
        //if it is in the new list but not in the old list, then add it
        it = newCompany.getUsers().listIterator();
        while (it.hasNext())
        {
            UserLoginDetails u = it.next();
            if (!u.isProcessed())
            {
                ulds.add(u);
                u.add(this);
                updated = true;
            }
        }
        return updated;
    }

    /**
     * @return the companyAuditList
     */
    @Transient
    public List<Company_AUDIT> getCompanyAuditList()
    {
        return companyAuditList;
    }

    /**
     * @param companyAuditList the companyAuditList to set
     */
    public void setCompanyAuditList(List<Company_AUDIT> companyAuditList)
    {
        this.companyAuditList = companyAuditList;
    }

    private void add(Company_AUDIT companyAudit)
    {
        if (companyAuditList == null)
        {
            companyAuditList = new LinkedList<Company_AUDIT>();
        }
        companyAuditList.add(companyAudit);
    }

    void removeUser(UserLoginDetails aThis)
    {
        if (users != null)
        {
            ListIterator<UserLoginDetails> it = users.listIterator();
            while (it.hasNext())
            {
                UserLoginDetails u = it.next();
                if (aThis.getId() == u.getId())
                {
                    users.remove(u);
                    break;//Out of loop
                }
            }
        }
    }

    public void logPrint(int level)
    {
        try
        {
            StringBuilder tab = new StringBuilder();
            for (int i = 0; i < level; i++)
            {
                tab.append("\t");
            }
            StringBuilder sb = new StringBuilder(tab.toString());
            sb.append("COMPANY\n");
            sb.append(tab.toString());
            sb.append("ID: \t");
            sb.append(id);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Code: \t");
            sb.append(code);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Name: \t");
            sb.append(name);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Desc: \t");
            sb.append(description);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Trading As: \t");
            sb.append(tradingAs);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Enabled: \t");
            sb.append(enabled);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Subscriber: \t");
            sb.append(subscriber);
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Street Address: \t");
            int l2 = level + 1;
            sb.append(streetAddress.logPrint(l2));
            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Postal Address: \t");
            sb.append(postalAddress.logPrint(l2));

            sb.append("\n");
            sb.append(tab.toString());
            sb.append("Users: \t");
            ListIterator<UserLoginDetails> it = users.listIterator();
            while (it.hasNext())
            {
                UserLoginDetails u = it.next();
                sb.append(u.logPrint(l2));
                sb.append("\n");
            }
            System.out.println(sb.toString());
        }
        catch (Exception e)
        {
            System.err.println(e.getMessage());
            e.printStackTrace();
        }
    }
}
